/** 
 * @file figures.h
 * @Synopsis  Contains classes for various figures
 * @author Kumar Anik
 * @version 1.0
 * @date 2010-06-25
 */
#include <iostream>
#include <vector>
#include <sstream>

using namespace std;

/** 
 * @Synopsis represents a point in co-ordinate frame 
 */
class Point {
public:
       Point() { };
       Point(int _x, int _y) : x(_x), y(_y) { };
      int x;
      int y;
};     
 
/** 
 * @Synopsis represents a polygon drawn using vertices 
 */
class Polygon {
      int numPoints;
      vector <Point> points;
      vector <Point> final;
      stringstream sout;
      bool animated;

public:  
      string color;
      string fillColor;
      int width;
      string name;

      int dur;                         /// These two variables are
      string repeatCount;	       /// for animation purposes. 
      
      void addPoint(int x, int y);
      void addAnimation(int x1, int y1, int x2, int y2);
                  
      string draw();
     
      Polygon() {
	    animated = false;
	    	
       	    color = "black";
	    fillColor = "none";
      	    width=3;

	    dur = 5;
	    repeatCount = "indefinite";
      }

      Point operator[] (int i) {
            return points[i];
      }
};

/** 
 * @Synopsis to draw ellipses and circles
 */
class Ellipse {
       Point center;
       int radius1;
       int radius2;
       stringstream sout;
       bool animated;	

public: 
       string color;
       string fillColor;
       int width;
       string name;
      
      Ellipse() {
       	    color = "black";
	    fillColor = "none";
      	    width=3;
      }
        
      Ellipse(int x, int y, int r1, int r2);

      string draw();
};

/** 
 * @Synopsis to draw curves  
 */
class Path {
private:
	stringstream sout;
public:
	
       string color;
       string fillColor;
       int width;
    
      Path() {
       	    color = "black";
	    fillColor = "none";
      	    width=3;
      }


	string draw();
	string returnPath();

	void addMoveTo(bool relative, int x, int y);
	void addLineTo(bool relative, int x, int y);
	void addHorizontalLineTo(bool relative, int x);
	void addVerticalLineTo(bool relative, int y);
	void addCurveTo(bool relative, int x1, int y1, int x2, int y2, int x, int y);
	//void addSmoothCurveTo(bool relative, int x2, int y2, int x, int y);
	void addQuadCurveTo(bool relative, int x1, int y1, int x, int y);
	//void addSmoothQuadCurveTo(bool relative, int x, int y);
	void addEllipticalArcTo(bool relative, int rx, int ry, int xRot, bool largeArcFlag, bool sweepFlag, int x, int y);
	void returnToInitial();
};
	

/** 
 * @Synopsis  it adds a point as vertex in the polygon
 * 
 * @Param x : x-coordinate of the new vertex
 * @Param y : y-cordinate for the new vertex
 */
void Polygon::addPoint(int x, int y)
{
     points.push_back(Point(x,y));
     final.push_back(Point(x,y));
}

/** 
 * @Synopsis  - add simple animation to a polygon
 * 
 * @Param x1 : initial value of x
 * @Param y1 : initial value of y
 * @Param x2 : final value of x
 * @Param y2 : final value of y
 */
void Polygon::addAnimation(int x1, int y1, int x2, int y2)
{
     animated = true;
     points.push_back(Point(x1,y1));
     final.push_back(Point(x2,y2));
}
          
/** 
 * @Synopsis  to draw the polygon
 * 
 * @Returns the generated SVG code  
 */
string Polygon::draw()
{
     int i = 0, numPoints = points.size();
     
     sout << "<polygon points=\" " << points[i].x << ", " << points[i].y << " ";
     
     for( i = 1; i < numPoints; i++ ) {
          sout << points[i].x << ", " << points[i].y << " ";
     }
     
     sout  << "\"\nstroke=\"" << color << "\" fill=\"" << fillColor 
	     << "\" stroke-width=\"" << width << "\" >\n";

     if ( animated ) {
	sout << "<animate attributeName=\"points\" from=\"";
	for( i = 0; i < numPoints; i++ ) {
		sout << points[i].x << "," << points[i].y << " ";
	}
	sout << "\" to=\"";
	for( i = 0; i < numPoints; i++ ) {
		sout << final[i].x << "," << final[i].y << " ";
	}
	sout << "\" dur=\"" << dur << "s\" repeatCount=\"" 
		<< repeatCount << "\"/>\n";
     
     }	
     sout << "</polygon>\n";

     if (!animated) {
	     for( i = 0; i < numPoints; i++ ) {
     	    	     sout << "<circle cx=\"" << points[i].x <<"\" cy=\"" << points[i].y 
                	     <<"\" r=\"3\" fill=\"red\" "
                             <<"\nstroke=\"black\" stroke-width=\"1\"/>\n";
     	     }
     } 
     
     return sout.str();
}     

/** 
 * @Synopsis  Constructor for ellipse
 * 
 * @Param x : x co-ordinate of the center
 * @Param y : y co-ordinate of the center
 * @Param r1 : x radius
 * @Param r2 : y radius
 */
Ellipse::Ellipse(int x, int y, int r1, int r2)
{
     color = "black";
     fillColor = "none";
     width=3;
     animated=false;
     
     center.x = x;
     center.y = y;
     radius1 = r1;
     radius2 = r2;
}

/** 
 * @Synopsis draw an ellipse 
 * 
 * @Returns the SVG code for the ellipse
 */
string Ellipse::draw()
{
     sout << "<ellipse cx=\"" << center.x <<"\" cy=\"" << center.y 
	     << "\" rx=\"" << radius1 <<"\" ry=\"" << radius2 
	     <<"\" fill=\"" << fillColor << "\"\nstroke=\"" 
	     << color << "\" stroke-width=\"" << width << "\">\n";

     sout << "</ellipse>\n";
     return sout.str();

}

/** 
 * @Synopsis  draw a path
 * 
 * @Returns   returns the SVG code for the path
 */
string Path::draw()		
{
	stringstream ret;
	ret << "<path d=\"";
	ret << sout.str();
	ret << "\"\nstroke=\"" << color << "\" fill=\"" << fillColor
	      << "\" stroke-width=\"" << width << "\"/>\n";
	
	return ret.str();
}

/** 
 * @Synopsis returs only the d-value of the path 
 * 
 * @Returns   SVG code of the d-value
 */
string Path::returnPath()
{
	return sout.str();
}

/** 
 * @Synopsis  add a shift origin command to the current path
 * 
 * @Param relative relative coordinates are relative or absolute
 * @Param x : x co-ordinate of the position
 * @Param y : y co-ordinate of the position
 */
void Path::addMoveTo(bool relative, int x, int y)
{
	sout << (relative ? "m" : "M") << x << "," << y << " ";
}

/** 
 * @Synopsis  add a linear move command to the current path
 * 
 * @Param relative relative coordinates are relative or absolute
 * @Param x : x co-ordinate of the position
 * @Param y : y co-ordinate of the position
 */
void Path::addLineTo(bool relative, int x, int y)
{
	sout << (relative ? "l" : "L") << x << "," << y << " ";
}

/** 
 * @Synopsis  move horizontally to a particular value
 * 
 * @Param relative relative coordinates are relative or absolute
 * @Param x : x co-ordinate of the position
 */
void Path::addHorizontalLineTo(bool relative, int x)
{
	sout << (relative ? "h" : "H") << x << " ";
}

/** 
 * @Synopsis  move vertically to a particular value
 * 
 * @Param relative relative coordinates are relative or absolute
 * @Param y : y co-ordinate of the position
 */
void Path::addVerticalLineTo(bool relative, int y)
{
	sout << (relative ? "v" :"V") <<  y << " ";
}

/** 
 * @Synopsis  adds a Bezier Cubic curve to the path
 * 
 * @Param relative relative coordinates are relative or absolute
 * @Param x1 : x co-ordinate of the control point 1
 * @Param y1 : y co-ordinate of the control point 1
 * @Param x2 : x co-ordinate of the control point 2
 * @Param y2 : y co-ordinate of the control point 2
 * @Param x : x co-ordinate of the end point
 * @Param y : y co-ordinate of the end point
 */
void Path::addCurveTo(bool relative, int x1, int y1, int x2, int y2, int x, int y)
{
	sout << (relative ? "c" :"C") << x1 << "," << y1 << " "
		<< x2 << "," << y2 << " " << x << "," << y << " ";
}

/*void Path::addSmoothCurveTo(bool relative, int x2, int y2, int x, int y);
{
	sout << (relative ? "s" : "S") << x2 << "," << y2 << " " 
		<< x << "," << y << " ";
}
*/

/** 
 * @Synopsis add a Bezeir Quadratic curve to the current path 
 * 
 * @Param relative coordinates are relative or absolute
 * @Param x1 : x co-ordinate of the control point
 * @Param y1 : y co-ordinate of the control point
 * @Param x : x co-ordinate of the end point
 * @Param y : y co-ordinate of the end point
 */
void Path::addQuadCurveTo(bool relative, int x1, int y1, int x, int y)
{
	sout << (relative ? "q" : "Q") << x1 << "," << y1 << " " 
		<< x << "," << y << " ";
}

/*
void Path::addSmoothQuadCurveTo(bool relative, int x, int y);
{
	sout << (relative ? "t" : "T") << x << "," << y << " ";
}
*/

/** 
 * @Synopsis  add an elliptical arc to the path definition
 * 
 * @Param relative co-ordinates are relative or absolutee
 * @Param rx : x-radius
 * @Param ry : y-radius
 * @Param xRot : rotation of ellipse in x-direction
 * @Param largeArcFlag : which arc? - See Documentation
 * @Param sweepFlag : which section? - See documentation
 * @Param x : x co-ordinate of the end point
 * @Param y : y co-ordinate of the end point
 */
void Path::addEllipticalArcTo(bool relative, int rx, int ry, int xRot, bool largeArcFlag, bool sweepFlag, int x, int y)
{
	sout << (relative ? "a" : "A") << rx << "," << ry << " " 
		<< xRot << " " << (int)largeArcFlag << " " 
		<< (int)sweepFlag << " " << x << "," << y << " "; 
}


/** 
 * @Synopsis  to add a Z in the end - means to close the path.
 */
void Path::returnToInitial()
{
	sout << "Z ";
}
